#include <iostream>
#include <sstream>

#include "filter.hpp"
#include "ary_str.hpp"
#include "http.hpp"
#include "at.hpp"
#include "sdp.hpp"
#include "defines.hpp"
#include "sys_var.hpp"

size_t http_write_callback(char *ptr, size_t size, size_t nmemb, void *obj )
{
	if( obj == nullptr ) return CURLE_OK;
	if( ptr == nullptr ) return CURLE_OK;

	http * __obj = reinterpret_cast< http*> ( obj );

	size_t len = size * nmemb;
#if defined( __DEBUG__ )
	std::cout << TYELLOW << "HTTP RECV DATA:" <<TGREEN << ptr << TNORMAL << std::endl;
#endif
	__obj->OnRecv( len , ptr , filterIO::OK );

	return CURLE_OK;
}
// ***************************************************************************************************************
http::http( filterList * l ) : filterIO( filterIO::tail , l )
{
	ptHeaders = nullptr;
	ptctx     = nullptr;

	CURLcode err = curl_global_init(CURL_GLOBAL_ALL);
	if( err == CURLE_OK ){
		ptctx = curl_easy_init();

		if( ptctx == nullptr ){
			std::cout << TRED << "Can not initiallize CURL lib interface." << TNORMAL << std::endl;
			abort();
		}
	}else{
		std::cout << TRED << "Can not initiallize CURL lib." << TNORMAL << std::endl;
		abort();
	}

	std::shared_ptr< CSysVar > ptvar = GetOrCreateSysVar();
	if( ptvar ){
		bool sdp_en = false;
		ptvar->GetValue( "/net/sdpen" , sdp_en );
		m_sdp_en = sdp_en;
	}
}

http::~http()
{
	if( ptHeaders ){
		curl_slist_free_all( ptHeaders );
	}

	if( ptctx )
		curl_easy_cleanup( ptctx );

	curl_global_cleanup();
}

http& http::server( const std::string& svr )
{
	m_server = svr;
	return *this;
}

http& http::url( const std::string& url )
{
	m_url = url;
	return *this;
}

http& http::head( const std::string& head )
{
	m_head = head;

	init_header_struct();

	return *this;
}

http& http::cert( const std::string& cert )
{
	m_cert = cert;
	return *this;
}

http& http::ssl( bool ssl )
{
	m_https = ssl;
	return *this;
}

http& http::port( int port )
{
	m_port = port;
	return *this;
}

http& http::keepalive( bool keep )
{
	m_keepalive = keep;
	return *this;
}

bool http::on_forward( size_t len , const char * data , err_code & err , size_t &rlen , char **rd )
{
	std::string str( data );
	if( IsInSerialAT() || str.find( "#AT") != std::string::npos ){
		*rd = ( char *)data;
		rlen = len;
		return false;
	}
	if( m_mtd == GET ){
		if( m_sdp_en == false ) do_get( data );
		else{
			sdp 	__sdp;
			char *  __data = nullptr; 				size_t __len = 0;
			if( __sdp.Unpack( data , len ) ){
				 int socket = __sdp.socket();
				 if( socket == 0x65 ){
					 __len = __sdp.GetData( (void**)&__data );
					 __data[ __len ] = '0';
					 do_get( __data );
				 }
			}
			if( __data ) free( __data );
		}

		rlen = len;
		*rd = ( char *)data;
	}else{
		if( m_sdp_en == false ){ do_post( data ); }
		else{
			sdp __sdp;
			char * __data = nullptr; 				size_t __len = 0;
			if( __sdp.Unpack( data , len ) ){
				 int socket = __sdp.socket();
				 if( socket == 0x65 ){
					 __len = __sdp.GetData((void**) &__data );
					 __data[ __len ] = '0';
					 do_post( __data );
				 }
			}
			if( __data ) free( __data );
		}

		rlen = len;
		*rd = (char*)data;
	}

	return false;
}

bool http::on_backward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd )
{
	bool ret = false;
	if( IsInSerialAT() ){
		*rd = ( char *)data;
		rlen = len;
		return false;
	}
	if( m_sdp_en == false ){
		rlen = len;
		*rd = ( char *)data;

		if( len > 0 && data != nullptr )
			ret = true;
	}else{
		sdp __sdp;
		__sdp.Net( (sdp::net)( 0x65 ) , data , len );
		void *__data = nullptr;
		rlen = __sdp.GetPackRaw( &__data );
		* rd = ( char *)__data;
		ret = true;
	}
	return ret;
}

int http::do_post( const char * data )
{
	int ret = 0;
	if( data == nullptr ){
		std::cout << TRED << __FILE__ << " [ " << __LINE__ << " ] data null" << std::endl;
		return -1;
	}
	std::string url;
	std::stringstream ss;
	curl_easy_setopt( ptctx , CURLOPT_WRITEDATA , this );
	ss << "http://" << m_server << ":" << m_port << m_url;
	url = ss.str();
	curl_easy_setopt( ptctx , CURLOPT_URL, url.c_str() );                      // 提交数据URL

	if( m_https == true ){   // 使用HTTPS
		curl_easy_setopt( ptctx , CURLOPT_PROTOCOLS, CURLPROTO_HTTPS );        // 使用HTTPS
		if( m_caPath.empty() ){                                                // 不验证服务器
			curl_easy_setopt(ptctx, CURLOPT_SSL_VERIFYPEER, false );
			curl_easy_setopt(ptctx, CURLOPT_SSL_VERIFYHOST, false );
		}else{//
			curl_easy_setopt(ptctx, CURLOPT_SSL_VERIFYPEER, true);              // 开启服务器验证
			curl_easy_setopt(ptctx , CURLOPT_CAINFO , m_caPath.c_str());        // 设置CA路径
		}
	}
	else{                    // 使用HTTP
		curl_easy_setopt( ptctx , CURLOPT_PROTOCOLS, CURLPROTO_HTTP );         // 使用HTTP协议
	}

	init_headers();                                                            // 设置头信息

	curl_easy_setopt( ptctx , CURLOPT_WRITEFUNCTION , http_write_callback );   // 设置接收数据回调处理函数
	curl_easy_setopt( ptctx , CURLOPT_HTTPPOST , 1 );                          // 提及方式为POST
	curl_easy_setopt( ptctx , CURLOPT_POSTFIELDS , data );					   // 要发送的数据
	curl_easy_setopt( ptctx , CURLOPT_TIMEOUT , m_respond_overtime );		   // 设置超时时间

	ret = curl_easy_perform( ptctx );
	return ret;
}

int http::do_post( const std::string& data )
{
	return do_post( data.c_str() );
}

int http::do_get( const char * data )
{
	int ret = 0;
	if( data == nullptr ){
		std::cout << TRED << __FILE__ << " [ " << __LINE__ << " ] data null." << std::endl;
		return -1 ;
	}
	std::string url;
	std::stringstream ss;
	curl_easy_setopt( ptctx , CURLOPT_WRITEDATA , this );
	ss << "http://" << m_server << ":" << m_port << m_url;
	url = ss.str();
	curl_easy_setopt( ptctx , CURLOPT_URL, url.c_str() );                      // 提交数据URL

	if( m_https == true ){   // 使用HTTPS
		curl_easy_setopt( ptctx , CURLOPT_PROTOCOLS, CURLPROTO_HTTPS );        // 使用HTTPS
		if( m_caPath.empty() ){                                                // 不验证服务器
			curl_easy_setopt(ptctx, CURLOPT_SSL_VERIFYPEER, false );
			curl_easy_setopt(ptctx, CURLOPT_SSL_VERIFYHOST, false );
		}else{//
			curl_easy_setopt(ptctx, CURLOPT_SSL_VERIFYPEER, true);              // 开启服务器验证
			curl_easy_setopt(ptctx, CURLOPT_CAINFO , m_caPath.c_str());       // 设置CA路径
		}
	}
	else{                   // 使用HTTP
		curl_easy_setopt( ptctx , CURLOPT_PROTOCOLS, CURLPROTO_HTTP );         // 使用HTTP协议
	}

	init_headers();                                                            // 设置头信息

	curl_easy_setopt( ptctx , CURLOPT_WRITEFUNCTION , http_write_callback );   // 设置接收数据回调处理函数
	curl_easy_setopt( ptctx , CURLOPT_HTTPGET , 1 );                           // 提及方式为POST
	curl_easy_setopt( ptctx , CURLOPT_POSTFIELDS , data );					   // 要发送的数据
	curl_easy_setopt( ptctx , CURLOPT_TIMEOUT , m_respond_overtime );		   // 设置超时时间

	ret = curl_easy_perform( ptctx );
	return ret;
}

int http::do_get( const std::string& data )
{
	return do_get( data.c_str() );
}

int http::init_headers()
{
	int ret = 0;

	if( ptHeaders )
		ret = curl_easy_setopt( ptctx , CURLOPT_HTTPHEADER , ptHeaders );

	return ret;

}

size_t http::find_line_pos( const std::string& str , size_t &line_width )
{
	size_t pos_ret = 0 , pos_nl = 0 , ret;
	pos_ret = str.find( "\r");
	pos_nl = str.find( "\n");
	if( pos_ret != std::string::npos && pos_nl != std::string::npos ){
		ret = pos_ret < pos_nl ? pos_ret : pos_ret;

		if( abs( (long)(pos_nl - pos_ret) ) == 1 ){
			line_width = 2;
		}else{
			line_width = 1;
		}
	}else{
		line_width = 1;
		ret = pos_nl;
	}

	return ret;
}

void http::init_header_struct()
{
	ArrayString  a_str;

	std::string   tmp = m_head , tmp1;
	ptHeaders = nullptr;
	// 分析头信息结构，头信息的设置是按行分的。所以首先将头信息按行分成不同的条目
	//
	std::stringstream ss;
	ss << m_head;

	while( ss.eof() == false ){//next_pos < tmp.length() ){
		std::getline( ss , tmp1 );
		a_str.push_back( tmp1 );
	}
	// 生成头信息内容结构体
	for( auto item : a_str ){
		curl_slist_append( ptHeaders , item.c_str());
	}
}
